/* Copyright (c) Microsoft Corporation.
   Licensed under the MIT License. */

/***************************************************************************

  browser.cpp

  Author: ******

  Date: March, 1995

	Review Status: Reviewed

  This file contains the browser display code.
  Browsers (abbrev Brws) include display, list and text classes.

	Studio Independent Browsers:
	BASE --> CMH --> GOK	-->	BRWD  (Browser display class)
	BRWD --> BRWL  (Browser list class; chunky based)
	BRWD --> BRWT  (Browser text class)
	BRWD --> BRWL --> BRWN  (Browser named list class)

	Studio Dependent Browsers:
	BRWD --> BRWR  (Roll call class)
	BRWD --> BRWT --> BRWA  (Browser action class)
	BRWD --> BRWL --> BRWP	(Browser prop/actor class)
	BRWD --> BRWL --> BRWB	(Browser background class)
	BRWD --> BRWL --> BRWC	(Browser camera class)
	BRWD --> BRWL --> BRWN --> BRWM (Browser music class)
	BRWD --> BRWL --> BRWN --> BRWM --> BRWI (Browser import sound class)
		
	Note: An "frm" refers to the displayed frames on any page.
	A "thum" is a generic Browser Thumbnail, which may be a
	chid, cno, cnoPar, gob, stn, etc.	A browser will display,
	over multiple pages, as many browser entities as there are
	thum's.

	This file contains the browser display code.

	To add additional browsers, create a derived class of
	the BRWD, BRWL or BRWT classes.
		
	If a browser is to be chunky file based, the BRWL class can be used.
	It includes GOKD chunks which are grandchildren of _ckiRoot (cnoNil
	implies wildcarding) and children of _ctgContent  - from .thd files
	entered in the registry of this product.
	The BRWL class allows the option of displaying either all the thumbnails
	of a particular ctg across registry specified directories (eg, scenes,
	actors)
		-or-
	of filling frames from GOKD thumbnails which are children of a single
	given chunk.

	Text class browsers (BRWT) create child TGOBs for each frame.

	Only cidBrowserCancel and cidBrowserOk exit the browser.
	cidBrowserSelect is selection only, not application of the selection.

	On creation of a new browser,
	 pcmd->rglw[0] = kid of Browser (type)
	 pcmd->rglw[1] = kid first frame.  Thumb kid is this + kidBrowserThumbOffset
	 pcmd->rglw[2] = kid of first control
	 pcmd->rglw[3] = x,y offsets

	Upon exiting, some browser classes retain BRCN (or derived from BRCN)
	context information for optimization.

	The kid of a single frame may be overridden (eg, for project help) using
	the override prids.

***************************************************************************/

#include "soc.h"
#include "studio.h"

ASSERTNAME

RTCLASS(BRCN)
RTCLASS(BRCNL)
RTCLASS(BRWD)
RTCLASS(BRWL)
RTCLASS(BRWT)
RTCLASS(BRWA)
RTCLASS(BRWP)
RTCLASS(BRWB)
RTCLASS(BRWC)
RTCLASS(BRWN)
RTCLASS(BRWM)
RTCLASS(BRWR)
RTCLASS(BRWI)
RTCLASS(BCL)
RTCLASS(BCLS)
RTCLASS(FNET)

BEGIN_CMD_MAP(BRWD, GOK)
	ON_CID_GEN(cidBrowserFwd, FCmdFwd, pvNil)
	ON_CID_GEN(cidBrowserBack, FCmdBack, pvNil)
	ON_CID_GEN(cidBrowserCancel, FCmdCancel, pvNil)
	ON_CID_GEN(cidBrowserOk, FCmdOk, pvNil)
	ON_CID_GEN(cidBrowserSelect, FCmdSelect, pvNil)
	ON_CID_GEN(cidBrowserSelectThum, FCmdSelectThum, pvNil)
	ON_CID_GEN(cidPortfolioFile, FCmdFile, pvNil)
	ON_CID_GEN(cidBrowserChangeCel, FCmdChangeCel, pvNil)
	ON_CID_GEN(cidBrowserDel, FCmdDel, pvNil)
END_CMD_MAP_NIL()


/****************************************************
 *
 * Create a browser display object
 *
 * Returns:
 *	A pointer to the object, else pvNil.
 *
 ****************************************************/
PBRWD BRWD::PbrwdNew(PRCA prca, long kidPar, long kidGlass)
{
	AssertPo(prca, 0);

	PBRWD pbrwd;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidPar, kidGlass))
		return pvNil;

	if ((pbrwd = NewObj BRWD(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwd->_FInitGok(prca, kidGlass))	
		{
		ReleasePpo(&pbrwd);
		return pvNil;
		}

	return pbrwd;
}

/****************************************************
 *
 * Build the GOB creation block
 *
 ****************************************************/
bool BRWD::_FBuildGcb(GCB *pgcb, long kidPar, long kidGlass)
{
	AssertVarMem(pgcb);

	PGOB pgobPar;
	RC rcRel;

	pgobPar = vapp.Pkwa()->PgobFromHid(kidPar);
	if (pvNil == pgobPar)
		{
		TrashVar(pgcb);
		return fFalse;
		}

#ifdef DEBUG
	Assert(pgobPar->FIs(kclsGOK), "Parent isn't a GOK");
	{
	PGOB pgob = vapp.Pkwa()->PgobFromHid(kidGlass);

	Assert(pgob == pvNil, "GOK already exists with given ID");
	}
#endif // DEBUG

	rcRel.Set(krelZero, krelZero, krelOne, krelOne);
	pgcb->Set(kidGlass, pgobPar, fgobNil, kginDefault, pvNil, &rcRel);
	return fTrue;
}

/****************************************************
 *
 * Initialize the Gok :
 * Do everything PgokNew would have done but didn't
 *
 ****************************************************/
bool BRWD::_FInitGok(PRCA prca, long kid)
{
	AssertBaseThis(0);
	AssertPo(prca, 0);

	if (!BRWD_PAR::_FInit(vapp.Pkwa(), kid, prca))
		return fFalse;
	if (!_FEnterState(ksnoInit))
		return fFalse;
	return fTrue;
}


/****************************************************
 *
 * Initialize a new browser object
 * Init knows how to either initialize or reinitialize
 * itself on reinstantiation of a browser.
 *
 * NOTE:  This does not display - see FDraw()
 *
 * On input,
 * ithumSelect is the thumbnail to be hilited
 * ithumDisplay is a thumbnail to be on the first
 *		page displayed
 * pcmd->rglw[0] = kid of Browser (type)
 * pcmd->rglw[1] = kid first frame.  Thumb kid is
 *	   this + kidBrowserThumbOffset
 * pcmd->rglw[2] = kid of first control
 * pcmd->rglw[3] = x,y offsets
 *
 ****************************************************/
void BRWD::Init(PCMD pcmd, long ithumSelect, long ithumDisplay, PSTDIO pstdio,
	bool fWrapScroll, long cthumScroll)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	// Split the initialization into two parts. The first part initializes the
	// state variables and can be done before the number of thumbnails on the
	// browser is known. The second part of the initialization relates to
	// browser display and requires the number of thumbnails to be known.

	_InitStateVars(pcmd, pstdio, fWrapScroll, cthumScroll);
	_InitFromData(pcmd, ithumSelect, ithumDisplay);
}

/****************************************************
 *
 * Initialize state variables for a new browser object.
 * A call will be made later to InitFromData to finish
 * the browser initialization based on thumbnail specific
 * information.
 *
 * On input,
 * pcmd->rglw[0] = kid of Browser (type)
 * pcmd->rglw[1] = kid first frame.  Thumb kid is
 *	   this + kidBrowserThumbOffset
 * pcmd->rglw[2] = kid of first control
 * pcmd->rglw[3] = x,y offsets
 *
 ****************************************************/
void BRWD::_InitStateVars(PCMD pcmd, PSTDIO pstdio,
	bool fWrapScroll, long cthumScroll)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	// Save parameters for this browser
	_pstdio = pstdio;
	_kidFrmFirst = pcmd->rglw[1];
	_kidControlFirst = pcmd->rglw[2];
	_dxpFrmOffset = SwHigh(pcmd->rglw[3]);
	_dypFrmOffset = SwLow(pcmd->rglw[3]);
	_cthumScroll = cthumScroll;
	_fWrapScroll = fWrapScroll;
	_fNoRepositionSel = fFalse;
}

/****************************************************
 *
 * Complete the initialization of  a new browser object.
 * The number of thumbnails for this browser has
 * already been determined. This call follows a
 * call to InitStateVars earlier.
 *
 * On input,
 * ithumSelect is the thumbnail to be hilited
 * ithumDisplay is a thumbnail to be on the first
 *		page displayed
 *
 ****************************************************/
void BRWD::_InitFromData(PCMD pcmd, long ithumSelect, long ithumDisplay)
{
	AssertThis(0);
	long cthum;

	// Context carryover
	if (_pbrcn != pvNil)
		{
		_pbrcn->brwdid = pcmd->rglw[0];
		}

	// Initialize variables
	cthum = _Cthum();
	AssertIn(ithumDisplay, -1, cthum);
	AssertIn(ithumSelect, -1, cthum);
	_ithumSelect = ithumSelect;
	_cfrmPageCur = 0;
	_cfrm = _CfrmCalc();
	_SetScrollState();		// Set the state of the scroll arrows

	// Adjust the display ifrm to begin at a page boundary
	if (ithumDisplay != ivNil)
		_ithumPageFirst = ithumDisplay;
	_SetVarForOverride();
	_CalcIthumPageFirst();
}


/****************************************************
 *
 * _SetVarForOverride : Projects may override kids
 *
 ****************************************************/
void BRWD::_SetVarForOverride(void)
{
	AssertThis(0);
	long thumOverride = -1;
	long thumSidOverride = -1;

	// Projects need to be able to hard wire one of the gob id's
	if (vpappb->FGetProp(kpridBrwsOverrideThum, &thumOverride) &&
		vpappb->FGetProp(kpridBrwsOverrideSidThum, &thumSidOverride) &&
		vpappb->FGetProp(kpridBrwsOverrideKidThum, &_kidThumOverride))
		{
		if (thumOverride >= 0)
			{
			_ithumOverride = _IthumFromThum(thumOverride, thumSidOverride);
			
			// Make sure this thum is on the displayed page
			_ithumPageFirst = _ithumOverride;	
			}
		else
			{
			_kidThumOverride = -1;
			_ithumOverride = -1;		
			}
		}
}


/****************************************************
 *
 * _GetThumFromIthum
 *
 ****************************************************/

void BRWD::_GetThumFromIthum(long ithum, void *pThumSelect, long *psid)
{
	AssertThis(0);
	AssertVarMem((long *)pThumSelect);
	AssertVarMem(psid);

	*((long *)pThumSelect) = ithum;
	TrashVar(psid);
}

/****************************************************
 *
 * Count the number of frames possible per page
 *
 ****************************************************/
long BRWD::_CfrmCalc(void)
{
	AssertThis(0);

	PGOB pgob;
	long ifrm;

	for (ifrm = 0; ; ifrm++)
		{
		//
		// If there is no GOKD parent, there are no more thumbnail
		// slots on this page
		//
		pgob = vapp.Pkwa()->PgobFromHid(_kidFrmFirst + ifrm);
		if (pvNil == pgob)
			{
			return ifrm;
			}
		AssertPo(pgob, 0);
		}
	return 0;
}

/****************************************************
 *
 * Determine the first thumbnail to display on the page
 *
 ****************************************************/
void BRWD::_CalcIthumPageFirst(void)
{
	AssertThis(0);
	long cthum = _Cthum();

	// Place the selection as close to the top as _cthumScroll permits
	if (!_fNoRepositionSel || _cthumScroll == ivNil)
		{
		if (_cthumScroll != ivNil)
			_ithumPageFirst = _ithumPageFirst - (_ithumPageFirst % _cthumScroll);
		else
			_ithumPageFirst = _ithumPageFirst - (_ithumPageFirst % _cfrm);
		}

	// Verify that the viewed thumbnail is within range
	if (_ithumPageFirst >= cthum)
		{
		if (cthum == 0)
			_ithumPageFirst = 0;
		else if (_cthumScroll != ivNil)
			_ithumPageFirst = (cthum - 1) - ((cthum - 1) % _cthumScroll);
		else
			_ithumPageFirst = (cthum - 1) - ((cthum - 1) % _cfrm);
		}

	// Display a full last page if not wrapping around
	if (!_fWrapScroll &&
		(_ithumPageFirst + _cfrm > cthum) &&
		_cthumScroll != ivNil)
		{
		while((_ithumPageFirst - _cthumScroll) + _cfrm >= cthum)
			_ithumPageFirst -= _cthumScroll;
		if (_ithumPageFirst	< 0)
			_ithumPageFirst = 0;
		}
}

/****************************************************
 *
 * Browser Display
 *
 ****************************************************/
bool BRWD::FDraw(void)
{
	AssertThis(0);

	PGOB pgobPar;
	GCB gcb;
	long ithum;
	long ifrm;
	long cthum = _Cthum();

	_CalcIthumPageFirst();

	// Begin a new page
	_cfrmPageCur = 0;

	for (ifrm = 0;  ifrm < _cfrm; ifrm++)
		{
		ithum = _ithumPageFirst + ifrm;
 		// Release the previouly attached thum from this frame
		_ReleaseThumFrame(ifrm);

		pgobPar = vapp.Pkwa()->PgobFromHid(_kidFrmFirst + ifrm);
 		if (pvNil == pgobPar)
			goto LContinue;
 		Assert(pgobPar->FIs(kclsGOK), "Invalid class");

		if (ithum >= cthum)
			{
			// Render invisible
			((PGOK)pgobPar)->FChangeState(kstBrowserInvisible);	// Ignore error
			_FClearHelp(ifrm); 	// Clear rollover help
			continue;
			}

		// Get next gob from list
		if (!_FSetThumFrame(ithum, pgobPar))
			goto LContinue;

		if(ithum == _ithumSelect)
			{
			((PGOK)pgobPar)->FChangeState(kstBrowserSelected);
			}
		else
			{
			((PGOK)pgobPar)->FChangeState(kstBrowserEnabled);
			}
LContinue:
		if (ithum < cthum)
			_cfrmPageCur++;
		}

	
	// Update page number
	if (pvNil != _ptgobPage)
		{
		long pagen;
		long cpage;
		STN stn;
		STN stnT;
		pagen = (_cfrm == 0) ? 0 : (_ithumPageFirst / _cfrm);
		cpage = ((cthum - 1) / _cfrm) + 1;
		_pstdio->GetStnMisc(idsBrowserPage, &stnT);
		stn.FFormat(&stnT, pagen + 1, cpage);
		_ptgobPage->SetText(&stn);
		}	

	return fTrue;
}


/****************************************************
 *
 * Find a unique hid for the current frame
 *
 ****************************************************/
long BRWD::_KidThumFromIfrm(long ifrm)
{
	AssertBaseThis(0);
	long kidThum;

	if (_ithumPageFirst + ifrm == _ithumOverride)
		kidThum = _kidThumOverride;
	else
		kidThum = GOB::HidUnique();

	return kidThum;
}


/****************************************************
 *
 * Compute the pgob of the parent for frame ifrm
 *
 ****************************************************/
PGOB BRWD::_PgobFromIfrm(long ifrm)
{
	AssertBaseThis(0);
	PGOB pgob;
	pgob = vapp.Pkwa()->PgobFromHid(_kidFrmFirst + ifrm);
	if (pvNil == pgob)
		return pvNil;
	return 	pgob->PgobLastChild();
}


/****************************************************
 *
 * Set the state of the scroll arrows for the Display
 *
 ****************************************************/
void BRWD::_SetScrollState(void)
{
	AssertThis(0);

	PGOB pgob;
	long st = (_Cthum() <= _cfrm) ? kstBrowserInvisible : kstBrowserEnabled;
 	
	pgob = vapp.Pkwa()->PgobFromHid(_kidControlFirst);
	if (pvNil != pgob)
		{
		Assert(pgob->FIs(kclsGOK), "Invalid class");
		if (!((PGOK)pgob)->FChangeState(st))
			Warn("Failed to change state Page Fwd button");
		}

	pgob = vapp.Pkwa()->PgobFromHid(_kidControlFirst + 1);
	if (pvNil != pgob)
		{
		Assert(pgob->FIs(kclsGOK), "Invalid class");
		if (!((PGOK)pgob)->FChangeState(st))
			Warn("Failed to change state Page Back button");
		}
	
}


/****************************************************
 *
 * Browser Command Handler : Browser Forward
 *
 ****************************************************/
bool BRWD::FCmdFwd(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);
	long cthumAdd;

	// Default _cthumScroll -> page scrolling
	if (ivNil == _cthumScroll)
		cthumAdd = _cfrm;
	else
		cthumAdd = _cthumScroll;

	// If either wrapping or there is more to see, increment first thumbnail index
	if (_fWrapScroll || (_ithumPageFirst + _cfrm < _Cthum()))
		_ithumPageFirst += cthumAdd;
	
	if (_ithumPageFirst >= _Cthum())
		{
		_ithumPageFirst = 0;
		}

	FDraw();	// Ignore failure

	return fTrue;
}

/****************************************************
 *
 * Browser Command Handler :
 * Set viewing page based on thumSelect == rglw[0]
 * and sid == rglw[1]
 * rglw[2] -> Hilite
 * rglw[3] -> Update Lists
 *
 ****************************************************/
bool BRWD::FCmdSelectThum(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	if (pcmd->rglw[3] != 0)
		{
		if (!_FUpdateLists())
			return fTrue;
		}

	if (pcmd->rglw[2] > 0)
		_ithumSelect = _IthumFromThum(pcmd->rglw[0], pcmd->rglw[1]);
	else
		_ithumSelect = -1;
	_ithumPageFirst = _ithumSelect - (_ithumSelect % _cfrm);

	_SetScrollState();		// Set the state of the scroll arrows
	FDraw();	// Ignore failure
	return fTrue;
}


/****************************************************
 *
 * Browser Command Handler : Browser Back
 * Scroll back one page in the browser
 *
 ****************************************************/
bool BRWD::FCmdBack(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	if (_ithumPageFirst == 0)
		{
		if (_fWrapScroll)
			{
			// Wrap back to the last page
			_ithumPageFirst = _Cthum() - (_Cthum() % _cfrm);
			}
		}
	else
		{
		// Normal non-wrap page scroll back
		if (ivNil == _cthumScroll)
			_ithumPageFirst -= _cfrmPageCur;
		else
			_ithumPageFirst -= _cthumScroll;

		}

	// FDraw updates _cfrmPageCur
	FDraw();		// Ignore failure
	return fTrue;
}

/****************************************************
 *
 * Browser Command Handler : Browser Frame Selected
 * (Thumbnail clicked)
 *
 * The script is expected to make the BrowserOK call
 * FCmdSelect does not exit
 *
 * pcmd->rglw[0] is browser id of thumb
 *
 ****************************************************/
bool BRWD::FCmdSelect(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	long ifrmSelect = pcmd->rglw[0] - _kidFrmFirst;

	_UnhiliteCurFrm();
	_ithumSelect = _ithumPageFirst + ifrmSelect;
	_FHiliteFrm(ifrmSelect);

	// Handle any derived class special actions (eg previews)
	_ProcessSelection();
	return fTrue;
}


/****************************************************
 *
 * Create Tgob's for any of the text based browsers
 * These will be destroyed when the browser exits
 * NOTE:: FCreateAllTgob() requires previous initialization
 * of the override parameters from BRWD::Init()
 *
 ****************************************************/	
bool BRWD::FCreateAllTgob(void)
{
	AssertThis(0);

	long ifrm;
	long hid;
	PTGOB ptgob;
	RC rcAbs;
	RC rcRel;

	for (ifrm = 0; ifrm < _cfrm; ifrm++)
			{
		if (_ithumPageFirst + ifrm == _ithumOverride)
			hid = _kidThumOverride;
		else
			hid = hidNil;
		
		ptgob = TGOB::PtgobCreate(_kidFrmFirst + ifrm, _idsFont, tavTop, hid);
		if (pvNil == ptgob)
			return fFalse;
		ptgob->SetAlign(tahLeft);

		ptgob->GetPos(&rcAbs, &rcRel);
		rcAbs.Inset(_dxpFrmOffset, _dypFrmOffset);
		ptgob->SetPos(&rcAbs, &rcRel);
		}

	return fTrue;
}


/****************************************************
 *
 * Hilite frame
 *
 ****************************************************/
bool BRWD::_FHiliteFrm(long ifrmSelect)
{
	AssertThis(0);
	AssertIn(ifrmSelect, 0, _cfrm);
	PGOB pgob;

	// Hilite currently selected frame
	AssertIn(ifrmSelect, 0, _cfrmPageCur);
	pgob = vapp.Pkwa()->PgobFromHid(_kidFrmFirst + ifrmSelect);
	if (pvNil == pgob)
		return fFalse;
		
	Assert(pgob->FIs(kclsGOK), "Invalid class");
	if (!((PGOK)pgob)->FChangeState(kstBrowserSelected))
		{
		_ithumSelect = ivNil;
		return fFalse;
		}
	return fTrue;
}

/****************************************************
 *
 * Unhilite currently selected Frame
 *
 ****************************************************/
void BRWD::_UnhiliteCurFrm(void)
{
	AssertThis(0);
	PGOB pgob;
	long ifrmSelectOld = _ithumSelect - _ithumPageFirst;

	// Unhilite currently selected frame
	if ((_ithumPageFirst <= _ithumSelect) && (_ithumPageFirst + _cfrmPageCur > _ithumSelect))
		{
		pgob = vapp.Pkwa()->PgobFromHid(_kidFrmFirst + ifrmSelectOld);
		if (pvNil == pgob)
			return;
		Assert(pgob->FIs(kclsGOK), "Invalid class");
		((PGOK)pgob)->FChangeState(kstBrowserEnabled);		// Ignore failure
		}
}

/****************************************************
 *
 * Browser Command Handler : Browser Cancel
 * Exit without applying selection
 *
 ****************************************************/
bool BRWD::FCmdCancel(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	vpsndm->StopAll();
	Release();			// OK/Cancel common cleanup

	return fTrue;
}


/****************************************************
 *
 * Browser cleanup for OK & Cancel
 *
 ****************************************************/
void BRWD::Release(void)
{
	_CacheContext();

	// Minimize the cache size on low mem machines
	_SetCbPcrmMin();
	BRWD_PAR::Release();
}		

/****************************************************
 *
 * Browser Command Handler : Browser Ok
 * Apply selection & exit
 *
 ****************************************************/
bool BRWD::FCmdOk(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);
	
	long sid;
	long thumSelect;

	_pstdio->SetDisplayCast(fFalse);

	if (ivNil != _ithumSelect)
		{
		// Get Selection from virtual function
		_GetThumFromIthum(_ithumSelect, &thumSelect, &sid);

		// Apply the selection (could take awhile)
		vapp.BeginLongOp();
		_ApplySelection(thumSelect, sid);
		vapp.EndLongOp();
		}

	// Cleanup & Dismiss browser
	Release();

	return fTrue;
}


/****************************************************
 *
 * BRWD _CacheContext
 *
 ****************************************************/
void BRWD::_CacheContext(void)
{
	if (_pbrcn != pvNil)
		_pbrcn->ithumPageFirst = _ithumPageFirst;
}


/****************************************************

   Browser Lists
   Derived from the BRWD display class

 ****************************************************/

/****************************************************
 *
 * Create a browser list object
 *
 * Returns:
 *  A pointer to the view, else pvNil.
 *
 ****************************************************/
PBRWL BRWL::PbrwlNew(PRCA prca, long kidPar, long kidGlass)
{
	AssertPo(prca, 0);

	PBRWL pbrwl;
	PGOK pgok;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidPar, kidGlass))
		return pvNil;

	if ((pbrwl = NewObj BRWL(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwl->_FInitGok(prca, kidGlass))	
		{
		ReleasePpo(&pbrwl);
		return pvNil;
		}

	//
	// Stop the studio action button animation while
	// any browser is up.
	//
	pgok = (PGOK)vapp.Pkwa()->PgobFromHid(kidActorsActionBrowser);

	if ((pgok != pvNil) && pgok->FIs(kclsGOK))
		{
		Assert(pgok->FIs(kclsGOK), "Invalid class");
		pgok->FChangeState(kstFreeze);
		}					

	return pbrwl;
}
							

/****************************************************
 *
 * Initialize a browser list object
 *
 * Returns:
 *  A pointer to the view, else pvNil.
 *
 * On Input,
 *	bws is the browser selection type
 *	thumSelect is the thumbnail to be hilited
 *
 ****************************************************/
bool BRWL::FInit(PCMD pcmd, BWS bws, long thumSelect, long sidSelect,
	CKI ckiRoot, CTG ctgContent, PSTDIO pstdio, PBRCNL pbrcnl,
	bool fWrapScroll, long cthumScroll)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	long ithumDisplay;
	long ccrf = 1;
	long ithumSelect;
	bool fBuildGl;

	_bws = bws;

   	_kidFrmFirst = pcmd->rglw[1];
	_cfrm = _CfrmCalc();

	// Initialize the state variables for the browser. This is required for
	// the creation of the tgobs beneath the call to _FCreateBuildThd below.
	// The call must be later followed by a call to BRWD::InitFromData().
	_InitStateVars(pcmd, pstdio, fWrapScroll, cthumScroll);

	if (pvNil == pbrcnl ||
		pbrcnl->brwdid == 0 ||
		pbrcnl->ckiRoot.cno != ckiRoot.cno)
		{
		fBuildGl = fTrue;

		// Cache the GOKD's by first creating a
		// chunky resource manager
		Assert(pvNil == _pcrm, "Logic error releasing pcrm");
		_pcrm = CRM::PcrmNew(ccrf);
		if (pvNil == _pcrm)
			goto LDismiss;
	
		if (!_FInitNew(pcmd, bws, thumSelect, ckiRoot, ctgContent))
			{
			goto LDismiss;
			}
		_cthumCD = _pglthd->IvMac();
		}
	else
		{
		// Reinitialize based on pbrcnl
		fBuildGl = fFalse;

		_cthumCD = pbrcnl->cthumCD;
		_pglthd = pbrcnl->pglthd;
		Assert(_cthumCD == _pglthd->IvMac(), "Logic error");
		_pglthd->AddRef();

		_pgst = pbrcnl->pgst;
		if (_pgst != pvNil)
			_pgst->AddRef();

		_pcrm = pbrcnl->pcrm;
		_pcrm->AddRef();
		for (long icrf = 0; icrf < _pcrm->Ccrf(); icrf++)
			_pcrm->PcrfGet(icrf)->SetCbMax(kcbMaxCrm);

		// First thumbnail on display page is inherited
		_ithumPageFirst = pbrcnl->ithumPageFirst;

		// This will make sure that we build any TGOBs
		if (!_FCreateBuildThd(ckiRoot, ctgContent, fBuildGl))
			goto LDismiss;
		}

	// Context carryover
	Assert(_pbrcn == pvNil, "Lost BRCN");
	_pbrcn = pbrcnl;
	if (pvNil != pbrcnl)
		{
		_pbrcn->AddRef();
		pbrcnl->ckiRoot = ckiRoot;
		if (fBuildGl)
			{
			// Rebuilding the GL ->
			// Remove old context
			// Save new context later

			/* Release */
			ReleasePpo(&pbrcnl->pglthd);
			ReleasePpo(&pbrcnl->pgst);
			ReleasePpo(&pbrcnl->pcrm);
			}
		}

	// Virtual function - used, eg., by sound browser to include user sounds
	_FUpdateLists();

	ithumSelect = _IthumFromThum(thumSelect, sidSelect);
  	if (fBuildGl)
		{
		// Display the selection if one exists
		if (ithumSelect != ivNil)
			ithumDisplay = ithumSelect;
		else
			ithumDisplay = _IthumFromThum(_thumDefault, _sidDefault);
		}
	else
		{
		// Display last page shown
		ithumDisplay = ivNil;
		}

	// Now initialize the display part of the browser.
	_InitFromData(pcmd, ithumSelect, ithumDisplay);

	vapp.DisableAccel();
	_fEnableAccel = fTrue;

	return fTrue;

LDismiss:
	return fFalse;
}


/****************************************************
 *
 * Initialization specific to the first time a list
 * browser is invoked
 *
 ****************************************************/
bool BRWL::_FInitNew(PCMD pcmd, BWS bws,
	long thumSelect, CKI ckiRoot, CTG ctgContent)
{
	AssertThis(0);

	// Selection type
	_bws = bws;

	// Set the display default
	_sidDefault = ((APP *)vpappb)->SidProduct();
	if (!vpappb->FGetProp(kpridBrwsDefaultThum, &_thumDefault))
		{
		Warn("couldn't get property kpridBrwsDefaultThum");
		_thumDefault = 0;
		}


	// Build the Thd
	if (!_FCreateBuildThd(ckiRoot, ctgContent))
		{
		return fFalse;
		}

	return fTrue;
}


/****************************************************
 *
 * Initialization
 * Called each time a browser is either first
 * instantiated or reinvoked.
 *
 * Each browser will have its own cache of gokd's.
 * The cache will vanish when the browser goes
 * away.
 *
 ****************************************************/
bool BRWL::_FCreateBuildThd(CKI ckiRoot, CTG ctgContent, bool fBuildGl)
{
	AssertThis(0);

	_fSinglePar = (ckiRoot.cno != cnoNil);
	_ckiRoot = ckiRoot;
	_ctgContent = ctgContent;

	if (fBuildGl)
		{
		//
		// Create the gl's
		//
		if (pvNil == (_pglthd = GL::PglNew(size(THD), kglthdGrow)))
			return fFalse;			
		_pglthd->SetMinGrow(kglthdGrow);
		}

	if (!_FGetContent(_pcrm, &ckiRoot, ctgContent, fBuildGl))
		goto LFail;

	if (_Cthum() == 0)
		{
		PushErc(ercSocNoThumbnails);
		return fFalse;	 	
		}

	if (fBuildGl)
		_SortThd();

	return fTrue;
LFail:
	return fFalse;
}


/****************************************************
 *
 * BRWL _FGetContent : Enum files & build the THD
 *
 ****************************************************/
bool BRWL::_FGetContent(PCRM pcrm, CKI *pcki, CTG ctg, bool fBuildGl)
{
	AssertThis(0);

	bool fRet = fFalse;
	PBCL pbcl = pvNil;

	if (!fBuildGl)
		return fTrue;

	//
	// Enumerate the files & build the THD
	//
	Assert(ctg != cnoNil || pcki->ctg == ctgNil, "Invalid browser call");

	pbcl = BCL::PbclNew(pcrm, pcki, ctg, _pglthd);
	if (pbcl == pvNil)
		goto LFail;

	/* We passed the pglthd and pgst in, so no need to get them back before
		releasing the BCLS */
	Assert(_pglthd->CactRef() > 1, "GL of THDs will be lost!");

	fRet = fTrue;
LFail:
	ReleasePpo(&pbcl);
	return fRet;
}


/****************************************************
 *
 * Get list selection from the ithum
 * Virtual function
 *									
 ****************************************************/
void BRWL::_GetThumFromIthum(long ithum, void *pvthumSelect, long *psid)
{
	AssertThis(0);
	AssertIn(ithum, 0, _Cthum());
	AssertVarMem(psid);

	THD thd;						
	
	if (_bws == kbwsIndex)
		{
		*psid = thd.tag.sid;
		*((long *)pvthumSelect) = ithum;
		return;
		}

	_pglthd->Get(ithum, &thd);
	*psid = thd.tag.sid;

	switch(_bws)
		{
	case kbwsChid:
		Assert(thd.chid != chidNil, "Bogus sort order for THD list");
		*((CHID *)pvthumSelect) = thd.chid;
		break;
	case kbwsCnoRoot:
		*((CNO *)pvthumSelect) = thd.tag.cno;
		break;
	default:
		Bug("Unhandled bws case");
		}

	return;
}


/****************************************************
 *
 * BRWL _CacheContext for reentry into the browser
 *
 ****************************************************/
void BRWL::_CacheContext(void)
{
	if (_pbrcn != pvNil)
		{
		PBRCNL pbrcnl = (PBRCNL)_pbrcn;

		Assert(pbrcnl->FIs(kclsBRCNL), "Bad context buffer");

		BRWL_PAR::_CacheContext();

		if (pbrcnl->pglthd == pvNil)
			{
			Assert(pbrcnl->pgst == pvNil, "Inconsistent state");
			Assert(pbrcnl->pcrm == pvNil, "Inconsistent state");

			/* Copy */
			pbrcnl->pglthd = _pglthd;
			pbrcnl->pgst = _pgst;
			pbrcnl->pcrm = _pcrm;

			/* AddRef */
			pbrcnl->pglthd->AddRef();
			if (pbrcnl->pgst != pvNil)
				pbrcnl->pgst->AddRef();
			pbrcnl->pcrm->AddRef();
			}
		else
			{
			Assert(pbrcnl->pglthd == _pglthd, "Inconsistent state");
			Assert(pbrcnl->pgst == _pgst, "Inconsistent state");
			Assert(pbrcnl->pcrm == _pcrm, "Inconsistent state");
			}

		/* Munge */
		/* Should never fail, since we should never be growing */
		AssertDo(pbrcnl->pglthd->FSetIvMac(_cthumCD),
			"Tried to grow pglthd (bad) and failed (also bad)");
		if (pbrcnl->pgst != pvNil)
			{
			long istn;

			istn = pbrcnl->pgst->IstnMac();
			while (istn > _cthumCD)
				pbrcnl->pgst->Delete(--istn);
			}
		for (long icrf = 0; icrf < _pcrm->Ccrf(); icrf++)
			_pcrm->PcrfGet(icrf)->SetCbMax(kcbMaxCrm);

		pbrcnl->cthumCD = _cthumCD;
		}
}


/****************************************************
 *
 * Sort the Thd gl by _bws
 * Uses bubble sort
 *
 ****************************************************/
void BRWL::_SortThd(void)
{
	AssertThis(0);

	long ithd;
	long jthd;
	long *plwJ, *plwI;
	THD thdi;
	THD thdj;
	long sid;
	long jthdMin = 0;
	bool fSortBySid;

	if (_bws == kbwsIndex)
		return;

	switch (_bws)
		{
	case kbwsChid:
		Assert(size(thdj.chidThum) == size(long), "Bad pointer cast");
		plwJ = (long *)&thdj.chidThum;
		plwI = (long *)&thdi.chidThum;
		fSortBySid = fFalse;
		break;
	case kbwsCnoRoot:
		Assert(size(thdj.tag.cno) == size(long), "Bad pointer cast");
		plwJ = (long *)&thdj.tag.cno;
		plwI = (long *)&thdi.tag.cno;
		fSortBySid = fTrue;
		break;
	default:
		Bug("Illegal _bws value");
		}

	if (fSortBySid && _pglthd->IvMac() > 0)
		{
		_pglthd->Get(0, &thdi);
		sid = thdi.tag.sid;
		}
	for (ithd = 1; ithd < _pglthd->IvMac(); ithd++)
		{
		_pglthd->Get(ithd, &thdi);
		if (fSortBySid && thdi.tag.sid != sid)
			{
			sid = thdi.tag.sid;
			jthdMin = ithd;
			}

		for (jthd = jthdMin; jthd < ithd; jthd++)
			{
			_pglthd->Get(jthd, &thdj);
			if (*plwJ < *plwI)
				continue;
			
			// Allow products to share identical content
			// but only view it once
			if (*plwJ == *plwI)
				{
				long ithdT;
				THD thdT;
				if (pvNil != _pgst && thdi.ithd < _pgst->IvMac())
					{
					Assert(thdi.ithd == ithd, "Logic error cleaning pgst");
					_pgst->Delete(thdi.ithd);
					}

				for (ithdT = ithd + 1; ithdT < _pglthd->IvMac(); ithdT++)
					{
					_pglthd->Get(ithdT, &thdT);
					thdT.ithd--;
					_pglthd->Put(ithdT, &thdT);
					}
#ifdef DEBUG
				// Note: Files for the current product was enumerated first.
				// This determines precedence.
				if (thdj.tag.sid == _sidDefault || thdi.tag.sid == _sidDefault)
					Assert(thdj.tag.sid == _sidDefault, "Browser deleting the wrong duplicate");
#endif //DEBUG
				_pglthd->Delete(ithd);
				ithd--;
				break;
				}	
			// Switch places
			_pglthd->Put(ithd, &thdj);
			_pglthd->Put(jthd, &thdi);
			_pglthd->Get(ithd, &thdi);
			}
		}
}


/****************************************************
 *
 * Get the index to the Thi for the given selection
 * Note: The selection thumSelect is a cno, chid, etc,
 * not an index into either pgl.
 *
 ****************************************************/
long BRWL::_IthumFromThum(long thumSelect, long sidSelect)
{
	AssertThis(0);		   

	long *plw;
	THD thd;

	if (thumSelect == ivNil)
		return ivNil;

	switch (_bws)
		{
	case kbwsIndex:
		return thumSelect;
		
	case kbwsChid:
		Assert(size(thd.chid) == size(long), "Bad pointer cast");
		plw = (long *)&thd.chid;
		break;

	case kbwsCnoRoot:
		Assert(size(thd.tag.cno) == size(long), "Bad pointer cast");
		plw = (long *)&thd.tag.cno;
		break;

	default:
		Assert(0,"Invalid _bws");
		}

	for (long ithd = 0; ithd < _pglthd->IvMac(); ithd++)
		{
		_pglthd->Get(ithd, &thd);
		if ((*plw == thumSelect) &&
				(sidSelect == ksidInvalid || thd.tag.sid == sidSelect))
			{
			return ithd;
			}
		}

	Warn("Selection not available");
	return ivNil;
}


/****************************************************
 *
 * Sets the ith Gob as a child of the current frame
 * Advance the gob (thumbnail) index
 *
 ****************************************************/	
bool BRWL::_FSetThumFrame(long ithd, PGOB pgobPar)
{
	AssertThis(0);
	AssertPo(pgobPar, 0);

	THD thd;
	PGOK pgok;
	RC rcAbs;
	RC rcRel;
	long kidThum;

	// Associate the gob with the current frame
	_pglthd->Get(ithd, &thd);
	
	kidThum = _KidThumFromIfrm(_cfrmPageCur);
	pgok = vapp.Pkwa()->PgokNew(pgobPar, kidThum, thd.cno, _pcrm);
	if (pvNil == pgok)
		return fFalse;

	((PGOB)pgok)->GetPos(&rcAbs, &rcRel);
	rcAbs.Offset(_dxpFrmOffset, _dypFrmOffset);
	((PGOB)pgok)->SetPos(&rcAbs, &rcRel);

	return fTrue;
}


/****************************************************
 *
 * Release previous thum from frame ifrm
 * Assumes gob based.
 *
 ****************************************************/	
void BRWL::_ReleaseThumFrame(long ifrm)
{
	AssertThis(0);
	PGOB pgob;

	// Release previous gob associated with the current frame
    pgob = _PgobFromIfrm(ifrm);
	if (pvNil != pgob)
		{
		ReleasePpo(&pgob);
		}
}


/****************************************************
 *
 * BCL class routines
 *
 ****************************************************/	
PBCL BCL::PbclNew(PCRM pcrm, CKI *pckiRoot, CTG ctgContent, PGL pglthd,
	bool fOnlineOnly)
{
	PBCL pbcl;

	pbcl = NewObj BCL;
	if (pbcl == pvNil)
		return pvNil;

	if (!pbcl->_FInit(pcrm, pckiRoot, ctgContent, pglthd))
		ReleasePpo(&pbcl);

	return pbcl;
}


bool BCLS::_FInit(PCRM pcrm, CKI *pckiRoot, CTG ctgContent, PGST pgst, PGL pglthd)
{
	AssertNilOrPo(pgst, 0);

	if (pgst == pvNil)
		{
		if ((pgst = GST::PgstNew()) == pvNil)
			goto LFail;
		}
	else
		pgst->AddRef();
	_pgst = pgst;

	if (!BCLS_PAR::_FInit(pcrm, pckiRoot, ctgContent, pglthd))
		goto LFail;

	return fTrue;
LFail:
	return fFalse;
}


bool BCL::_FInit(PCRM pcrm, CKI *pckiRoot, CTG ctgContent, PGL pglthd)
{
	AssertNilOrPo(pcrm, 0);
	Assert(pckiRoot->ctg != ctgNil, "Bad CKI");
	AssertNilOrPo(pglthd, 0);

	if (pglthd == pvNil)
		{
		if ((pglthd = GL::PglNew(size(THD))) == pvNil)
			goto LFail;
		}
	else
		pglthd->AddRef();
	_pglthd = pglthd;

	_ctgRoot = pckiRoot->ctg;
	_cnoRoot = pckiRoot->cno;
	_ctgContent = ctgContent;
	_fDescend = _cnoRoot != cnoNil;
	Assert(!_fDescend || _ctgContent != ctgNil, "Bad initialization");

	if (!_FBuildThd(pcrm))
		goto LFail;

	return fTrue;
LFail:
	return fFalse;
}


PBCLS BCLS::PbclsNew(PCRM pcrm, CKI *pckiRoot, CTG ctgContent, PGL pglthd,
	PGST pgst, bool fOnlineOnly)
{
	PBCLS pbcls;

	pbcls = NewObj BCLS;
	if (pbcls == pvNil)
		goto LFail;

	if (!pbcls->_FInit(pcrm, pckiRoot, ctgContent, pgst, pglthd))
		ReleasePpo(&pbcls);

LFail:
	return pbcls;
}


/****************************************************
 *
 * Enumerate thumbnail files & create the THD
 * (Thumbnail descriptor gl)
 *
 * Note: In the case of actions & views, this is one
 * entry.
 * Input: fBuildGl is false if only the pcrm requires
 * initialization; otherwise the _pglthd is built.
 *
 * Sort based on _bws (browser selection flag)
 *
 ****************************************************/
bool BCL::_FBuildThd(PCRM pcrm)
{
	AssertThis(0);
	AssertNilOrPo(pcrm, 0);

	bool fRet = fTrue;
	PCFL pcfl;
	long sid;
	FNET fnet;
	FNI fniThd;

	if (!fnet.FInit())
		return fFalse;
	while(fnet.FNext(&fniThd, &sid) && fRet)
		{
		if (fniThd.Ftg() != kftgThumbDesc)
			continue;
		pcfl = CFL::PcflOpen(&fniThd, fcflNil);
		if (pvNil == pcfl)
			{
			// Error reported elsewhere
			continue;		
			}

		/* Don't use this file if it doesn't have what we're looking for */
		if (_fDescend ? !pcfl->FFind(_ctgRoot, _cnoRoot) :
				pcfl->CckiCtg(_ctgRoot) == 0)
			{
			goto LContinue;
			}

		// Add the file to the crm
		if (pcrm != pvNil && !pcrm->FAddCfl(pcfl, kcbMaxCrm))
			{
			// fatal error
			fRet = fFalse;
			goto LContinue;
			}
		
		if (!_FAddFileToThd(pcfl, sid))
			{
			// Error issued elsewhere
			fRet = fFalse;
			goto LContinue;
			}

LContinue:
		ReleasePpo(&pcfl);
		}

	return fRet;
}


/****************************************************
 *
 *  Add the chunks of file pcfl to the THD
 *
 *	Requires pre-definition of _ckiRoot, _ctgContent
 *
 *	Filtering is based on ctg, cno, but not chid
 *	A value of cnoNil => wild card search
 *
 ****************************************************/
bool BCL::_FAddFileToThd(PCFL pcfl, long sid)
{
	AssertThis(0);
	AssertPo(pcfl, 0);

	long ickiRoot;
	long cckiRoot;
	CKI ckiRoot;
	KID kidPar;
	long ikidPar;	
	long ckidPar;
	
	Assert(ctgNil != _ctgRoot, "Illegal call");

	if (!_fDescend)
		cckiRoot = pcfl->CckiCtg(_ctgRoot);
	else
		{
		ckiRoot.ctg = _ctgRoot;
		ckiRoot.cno = _cnoRoot;
		cckiRoot = 1;
		}

	// For each grandparent, define ckiRoot
	for (ickiRoot = 0; ickiRoot < cckiRoot; ickiRoot++)
		{
		if (!_fDescend)
			{
			// Get the cki of the grandparent
			if (!pcfl->FGetCkiCtg(_ctgRoot, ickiRoot, &ckiRoot))	
				return fFalse;
	
			//
			// If only one level of parent specified in the search
			// then add the gokd from this parent and continue
			//
			if (!_FAddGokdToThd(pcfl, sid, &ckiRoot))
				return fFalse;
			continue;
			}

		//
		// Drop down one more level
		//
		if (!pcfl->FFind(ckiRoot.ctg, ckiRoot.cno))
			continue;
	
		ckidPar = pcfl->Ckid(ckiRoot.ctg, ckiRoot.cno);

		for (ikidPar = 0; ikidPar < ckidPar; ikidPar++)
			{
			if (!pcfl->FGetKid(ckiRoot.ctg, ckiRoot.cno, ikidPar, &kidPar))
				return fFalse;

			if (kidPar.cki.ctg != _ctgContent)
				continue;												

			// On failure, continue to add other files
			// Error reported elsewhere
			_FAddGokdToThd(pcfl, sid, &kidPar);
			}
	  	}
	
	return fTrue;
}


bool BCL::_FAddGokdToThd(PCFL pcfl, long sid, CKI *pcki)
{
	KID kid;

	kid.cki = *pcki;
	kid.chid = chidNil;
	return _FAddGokdToThd(pcfl, sid, &kid);
}


/****************************************************
 *
 *  Add a single GOKD to the THD
 *	The GOKD is a child of ckiPar.
 *  cnoPar is read from the ckiPar chunk
 *
 ****************************************************/
bool BCL::_FAddGokdToThd(PCFL pcfl, long sid, KID *pkid)
{
	AssertThis(0);
	AssertPo(pcfl, 0);
	AssertVarMem(pkid);

	CKI cki = pkid->cki;
	KID kid;
	THD thd;
	BLCK blck;
	TFC tfc;

	// Read the Par chunk to find the cno of the CD content
	if (!pcfl->FFind(cki.ctg, cki.cno, &blck) ||
		!blck.FUnpackData())
		{
		goto LFail;
		}
	if (blck.Cb() != size(TFC))
		goto LFail;
	if (!blck.FReadRgb(&tfc, size(TFC), 0))
		goto LFail;
	if (kboCur != tfc.bo)
		SwapBytesBom(&tfc, kbomTfc);
	Assert(kboCur == tfc.bo, "bad TFC");
	if (!_fDescend)
		{
		thd.tag.cno = tfc.cno;
		thd.tag.ctg = tfc.ctg;
		}
	else
		{
		thd.tag.ctg = tfc.ctg;
		thd.chid = tfc.chid;
		}

	Assert(thd.grfontMask == tfc.grfontMask, "Font style browser broken");
	Assert(thd.grfont == tfc.grfont, "Font style browser broken");

	thd.tag.sid = sid;
	thd.chidThum = pkid->chid;
	if (pcfl->FGetKidChidCtg(cki.ctg, cki.cno, 0, kctgGokd, &kid))
		thd.cno = kid.cki.cno;
	else
		{
		// If there are no GOKD children, enter the reference to the named
		// parent chunk
		thd.cno = tfc.cno;
		}

	thd.ithd = _pglthd->IvMac();
	if (!_pglthd->FInsert(thd.ithd, &thd))
		goto LFail;

	return fTrue;
LFail:
	return fFalse;
}



bool BCLS::_FAddGokdToThd(PCFL pcfl, long sid, KID *pkid)
{
	AssertThis(0);
	AssertPo(pcfl, 0);
	AssertVarMem(pkid);

	if (!_FSetNameGst(pcfl, pkid->cki.ctg, pkid->cki.cno))
		goto LFail;

	if (!BCLS_PAR::_FAddGokdToThd(pcfl, sid, pkid))
		goto LFail;

	return fTrue;
LFail:
	return fFalse;
}


/****************************************************
 *
 * Save the name of the Par chunk in the Gst
 *
 ****************************************************/
bool BCLS::_FSetNameGst(PCFL pcfl, CTG ctg, CNO cno)
{
	AssertThis(0);
	AssertPo(pcfl, 0);
	STN stn;

	if (!pcfl->FGetName(ctg, cno, &stn))
		return fFalse;
	if (!_pgst->FAddStn(&stn))
		return fFalse;
	return fTrue;
}

/****************************************************
 *
 * Enumerate thumbnail files
 *
 * Initialization
 *
 ****************************************************/
bool FNET::FInit(void)
{
	AssertThis(0);

	FTG ftgThd = kftgThumbDesc;

	vapp.GetFniProduct(&_fniDirProduct); // look for THD files in the product FIRST
	_fniDir = _fniDirProduct;
	_fniDirMSK = _fniDirProduct;
	_fInitMSKDir = fTrue;

	if (!_fniDirMSK.FUpDir(pvNil, ffniMoveToDir))
		return fFalse;
 	if (!_fne.FInit(&_fniDir, &ftgThd, 1))
		return fFalse;
	_fInited = fTrue;
	return fTrue;
}


/****************************************************
 *
 * Enumerate thumbnail files
 *
 * Return the fni & (optionally) the sid
 * Note: _idir == 0 -> current product
 *
 ****************************************************/
bool FNET::FNext(FNI *pfni, long *psid)
{
	AssertThis(0);
	AssertPo(pfni, 0);
	AssertNilOrVarMem(psid);

	FTG ftgThd = kftgThumbDesc;
	FTG ftgDir = kftgDir;
	STN stnProduct;

	if (!_fInited)
		return fFalse;

	// Return files from currently enumerated directory
	// Note: This returns files from current product before 
	// initializing _fneDir
	if (_FNextFni(pfni, psid))
		return fTrue;

	if (_fInitMSKDir)
		{
		if (!_fneDir.FInit(&_fniDirMSK, &ftgDir, 1))
			return fFalse;
		_fInitMSKDir = fFalse;		
		}

	while (_fneDir.FNextFni(&_fniDir))
		{
		if (_fniDir.FSameDir(&_fniDirProduct)) 	// already enumerated
			continue;

		if (!_fne.FInit(&_fniDir, &ftgThd, 1))	// Initialize the file enumeration
			return fFalse;
		if(_FNextFni(pfni, psid))
			return fTrue;
		}

	return fFalse;						// all done		
}

/****************************************************
 *
 * Gets the file from the current FNET enumeration
 * This uses the current fne.
 *
 ****************************************************/
bool FNET::_FNextFni(FNI *pfni, long *psid)
{
	AssertThis(0);
	STN stnProduct;

	Assert(_fniDir.Ftg() == kftgDir, "Logic error");
	if (pvNil != psid)
		{
		if (!_fniDir.FUpDir(&stnProduct, ffniNil))
			return fFalse;
		if (!vptagm->FGetSid(&stnProduct, psid))		
			return fFalse;
		}

	return (_fne.FNextFni(pfni));
}


/****************************************************
 *
 * BRWN Initialization
 * -> BRWL Initialization plus tgob creation
 *
 ****************************************************/	
bool BRWN::FInit(PCMD pcmd, BWS bws, long thumSelect, long sidSelect,
	CKI ckiRoot, CTG ctgContent, PSTDIO pstdio, PBRCNL pbrcnl,
	bool fWrapScroll, long cthumScroll)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	if (!BRWN_PAR::FInit(pcmd, bws, thumSelect, sidSelect, ckiRoot, ctgContent,
		pstdio, pbrcnl, fWrapScroll, cthumScroll))
		{
		return fFalse;
		}	

	if (!FCreateAllTgob())
		return fFalse;
	return fTrue;
}

/****************************************************
 *
 * Build the thd
 *
 ****************************************************/	
bool BRWN::_FGetContent(PCRM pcrm, CKI *pcki, CTG ctg, bool fBuildGl)
{
	AssertThis(0);

	bool fRet = fFalse;
	PBCLS pbcls = pvNil;

	if (!fBuildGl)
		{
		return fTrue;
		}

	if (pvNil == (_pgst = GST::PgstNew(0)))
		goto LFail;

	//
	// Enumerate the files & build the THD
	//
	Assert(ctg != cnoNil || pcki->ctg == ctgNil, "Invalid browser call");

	pbcls = BCLS::PbclsNew(pcrm, pcki, ctg, _pglthd, _pgst);
	if (pbcls == pvNil)
		goto LFail;

	/* We passed the pglthd and pgst in, so no need to get them back before
		releasing the BCLS */
	Assert(_pglthd->CactRef() > 1, "GL of THDs will be lost!");
	Assert(_pgst->CactRef() > 1, "GST will be lost!");

	fRet = fTrue;
LFail:
	ReleasePpo(&pbcls);
	return fRet;
}


/****************************************************
 *
 * Removes the text from the unused tgob
 *
 ****************************************************/	
void BRWN::_ReleaseThumFrame(long ifrm)
{
	AssertThis(0);
	AssertIn(ifrm, 0, _cfrm);

	STN stn;
	PGOB pgob;

	pgob = _PgobFromIfrm(ifrm);
	if (pvNil != pgob)
		{
		stn.SetNil();
		((PTGOB)pgob)->SetText(&stn);
		}

	// The BRWN class retains the tgob while the browser is up
}

/****************************************************
 *
 * Sets the ith text item in the child of the
 * current frame
 *
 ****************************************************/	
bool BRWN::_FSetThumFrame(long ithd, PGOB pgobPar)
{
	AssertThis(0);
	AssertIn(ithd, 0, _pglthd->IvMac());
	AssertPo(pgobPar, 0);

	PTGOB ptgob;
	STN stnLabel;
	THD thd;

	_pglthd->Get(ithd, &thd);
	_pgst->GetStn(thd.ithd, &stnLabel);
	ptgob = (PTGOB)(pgobPar->PgobFirstChild());

	Assert(pvNil != ptgob, "No TGOB for the text");
	if (pvNil != ptgob)
		ptgob->SetText(&stnLabel);

	return fTrue;
}


/****************************************************
 *
 * Browser Command Handler : Browser Ok
 * Apply selection & exit
 *
 ****************************************************/
bool BRWN::FCmdOk(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	if (ivNil == _ithumSelect)
		{
		TAG tag;
		PMVU pmvu;
		pmvu = (PMVU)(_pstdio->Pmvie()->PddgGet(0));
		tag.sid = ksidInvalid;
		tag.pcrf = pvNil;
	 	pmvu->SetTagTool(&tag);	  	// No need to close tag with ksidInvalid
		}

	if (BRWD::FCmdOk(pcmd))
		{
		//
		// Stop any playing sounds in a sound browser.  We do
		// not stop playing sounds in non-sound browsers,
		// because the selection sound may be playing.
		//
		vpsndm->StopAll();
		return fTrue;	
		}

	return fFalse;
}

/****************************************************
 *
 * Create a BRoWser Music Sound object
 *
 ****************************************************/
PBRWM BRWM::PbrwmNew(PRCA prca, long kidGlass, long sty, PSTDIO pstdio)
{
	AssertPo(prca, 0);
	AssertPo(pstdio, 0);

	PBRWM pbrwm;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidBackground, kidGlass))
		return pvNil;

	if ((pbrwm = NewObj BRWM(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwm->_FInitGok(prca, kidGlass))	
		{
		ReleasePpo(&pbrwm);
		return pvNil;
		}

	pbrwm->_sty = sty;
	pbrwm->_pstdio = pstdio;
	if (!pstdio->Pmvie()->FEnsureAutosave(&pbrwm->_pcrf))
		{
		ReleasePpo(&pbrwm);
		return pvNil;
		}

	pbrwm->_ptgobPage = TGOB::PtgobCreate(kidBrowserPageNum, idsBrwsPageFont, tavCenter);
	return pbrwm;
}

/****************************************************
 *
 * Add all of the sounds of the movie from _pcrf
 * to the current BRWL lists:
 *	pglthd, pgst
 *
 * Used at initialization time by both the BRWI and
 * the BRWM classes.
 *
 * It is assumed that initialization has created
 * (if empty) lists at this point.
 *
 ****************************************************/
bool BRWM::_FUpdateLists(void)
{
	AssertThis(0);

	STN stn;
	THD thd;
	long ithd;
	long ithdOld;
	long ccki;
	long icki;
	CKI cki;
	TAG tag;
	PMSND pmsnd = pvNil;
	PCFL pcfl = _pcrf->Pcfl();

	// Enum through current movie for user sounds
	// For each	one, extend the lists to include the new sound

	ccki = pcfl->CckiCtg(kctgMsnd);
	for (icki = 0; icki < ccki; icki++)
		{
		if (!pcfl->FGetCkiCtg(kctgMsnd, icki, &cki))
			goto LNext;
		tag.sid = ksidUseCrf;  // Non-CD-loaded content
		tag.pcrf = _pcrf;
		tag.ctg = kctgMsnd;
		tag.cno = cki.cno;

		// Read the msnd chunk and continue if sty's do not match
		pmsnd = (PMSND)vptagm->PbacoFetch(&tag, MSND::FReadMsnd);
		if (pvNil == pmsnd)
			goto LNext;

		// Don't add user sounds multiple times on mult reinstantiations
		if (_FSndListed(cki.cno, &ithdOld))
			{
			if (pmsnd->FValid())
				goto LNext;
			// Remove invalid snds from the lists
			_pglthd->Delete(ithdOld);
			_pgst->Delete(ithdOld);
			for (ithd = ithdOld; ithd < _pglthd->IvMac(); ithd++)
				{
				_pglthd->Get(ithd, &thd);
				thd.ithd--;
				_pglthd->Put(ithd, &thd);
				}
			goto LNext;
			}

		if (_sty != pmsnd->Sty())
			goto LNext;

		if (!pmsnd->FValid())
			goto LNext;

		if (!pcfl->FGetName(kctgMsnd, cki.cno, &stn))
			goto LNext;

		if (!_FAddThd(&stn, &cki))
			goto LNext;

LNext:
		ReleasePpo(&pmsnd);
		}
	return fTrue;
}


/****************************************************
 *
 * Test to see if a sound is already in the lists
 *
 ****************************************************/
bool BRWM::_FSndListed(CNO cno, long *pithd)
{
	AssertBaseThis(0);

	long ithd;
	THD *pthd;

	for (ithd = _cthumCD; ithd < _pglthd->IvMac(); ithd++)
		{
		pthd = (THD *)_pglthd->QvGet(ithd);
		if (pthd->tag.cno == cno)
			{
			if (pvNil != pithd)
				*pithd = ithd;
			return fTrue;
			}
	    }
	return fFalse;
}

/****************************************************
 *
 * Extend the BRWL lists
 *
 ****************************************************/
bool BRWM::_FAddThd(STN *pstn, CKI *pcki)
{
	AssertBaseThis(0);
	THD thd;

	if (!_pgst->FAddStn(pstn))
		return fFalse;;
		
	thd.tag.sid = ksidUseCrf;
	thd.tag.pcrf = _pcrf;
	thd.tag.ctg = pcki->ctg;
	thd.tag.cno = pcki->cno;
	thd.cno = cnoNil; 		//unused
	thd.chidThum = chidNil; //unused
	thd.ithd = _pglthd->IvMac();
	if (!_pglthd->FAdd(&thd))
		goto LFail1;
	
	return fTrue;
LFail1:
	_pgst->Delete(_pgst->IstnMac() - 1);
	return fFalse;
}


/****************************************************
 *
 * Process Browser Music Selection
 * Process selection from either main sound browser or
 * Import sound browser
 *
 ****************************************************/
void BRWM::_ProcessSelection(void)
{
	AssertThis(0);
	PMSND pmsnd;
	TAG tag;
	long thumSelect;
	long sid;

 	// Get Selection from virtual function
	_GetThumFromIthum(_ithumSelect, &thumSelect, &sid);

	tag.sid = sid;
	tag.pcrf = (ksidUseCrf == sid) ? _pcrf : pvNil;
	tag.ctg = kctgMsnd;
	tag.cno = (CNO)thumSelect;

	if (!vptagm->FCacheTagToHD(&tag))
		{
		Warn("cache tag failed");
		return;
		}

	pmsnd = (PMSND)vptagm->PbacoFetch(&tag, MSND::FReadMsnd);

	if (pvNil == pmsnd)
		return;
	pmsnd->Play(0, fFalse, fFalse, pmsnd->Vlm(), fTrue);
	ReleasePpo(&pmsnd);

	return;
}


/****************************************************
 *
 * Browser Command Handler : (For importing sounds)
 * This command will be generated following portfolio
 * execution
 *
 ****************************************************/
bool BRWM::FCmdFile(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	FNI fni;
	long kidBrws;
	PFIL pfil = pvNil;	// Wave or midi file
	PCFL pcfl = pvNil;	// Movie file
	long icki;
	long ccki;
	STN stn;
	CKI cki;
	CMD cmd;

	
	vapp.GetPortfolioDoc(&fni);
	vpappb->BeginLongOp();

	switch(fni.Ftg())
		{
	case kftgMidi:
		// The non-score browsers only import wav files
		Assert(_sty == styMidi, "Portfolio should filter out Wave for this browser");
		if (_sty != styMidi)
			goto LEnd;
		cki.ctg = kctgMidi;
		break;

	case kftgWave:
		Assert(_sty != styMidi, "Portfolio should filter out Midi for this browser");
		// The score browser does not import wave files
		if (_sty == styMidi)
			goto LEnd;
		cki.ctg = kctgWave;
		break;

	case kftg3mm:
	default:
		// Import <user> sounds from a movie
		// Verify version numbers before accepting this file
		pcfl = CFL::PcflOpen(&fni, fcflNil);

		if(pvNil == pcfl)
			goto LEnd;
		if (!_pstdio->Pmvie()->FVerifyVersion(pcfl))
			{
			goto LEnd;	 	// Erc already pushed
			}

		ccki = pcfl->CckiCtg(kctgMsnd);
		if (ccki == 0)
			{
			// There may be sounds in the new movie, but no user sounds
			PushErc(ercSocNoKidSndsInMovie);   		
			goto LEnd;
			}
		
		// Are the user sounds valid?
		for (icki = 0; icki < ccki; icki++)
			{
			bool fInvalid;
			long sty;
			if (!pcfl->FGetCkiCtg(kctgMsnd, icki, &cki))
				goto LEnd;
			if (!MSND::FGetMsndInfo(pcfl, kctgMsnd, cki.cno, &fInvalid, &sty))
				goto LEnd;
			
			if (!fInvalid && sty == _sty)
				goto LValidSnds;  	// There are valid user snds
			}

		PushErc(ercSocNoKidSndsInMovie);   		
		goto LEnd;

LValidSnds:
		// Bring up a new import browser on top of this one
		// Launch the script
		switch(_sty)
			{
		case stySfx:
			kidBrws = kidBrwsImportFX;	
			break;
		case stySpeech:
			kidBrws = kidBrwsImportSpeech;
			break;
		case styMidi:
			kidBrws = kidBrwsImportMidi;
			break;
		default:
			Assert(0, "Invalid _sty in browser");
			break;
			}
		// Tell the script to launch the import browser & send a
		// cidBrowserReady to kidBrws when ready
		vpcex->EnqueueCid(cidLaunchImport, pvNil, pvNil, kidBrws);
		goto LEnd;
		break;	
		}

	// Import sound from a wave or midi file
	pfil = FIL::PfilOpen(&fni);
	if (pvNil == pfil)
		goto LEnd;  	// Error will have been reported

	// Read the file & store as a chunk	in the current movie
	if (!_pstdio->Pmvie()->FCopySndFileToMvie(pfil, _sty, &cki.cno))
		goto LEnd;		// Error will have been reported

	// Add (eg) the movie sounds from &fni to the current
	// browser lists, which are BRWL derived.
	cki.ctg = kctgMsnd;
	fni.GetLeaf(&stn);	 	// name of sound
	if (!_FAddThd(&stn, &cki))
		goto LEnd;

	// Select the item, extend lists, hilite it	& redraw
	cmd.rglw[0] = cki.cno;
	cmd.rglw[1] = ksidUseCrf;
	cmd.rglw[2] = 1; 	// Hilite
	cmd.rglw[3] = 0;	// Lists already updated
	if (!FCmdSelectThum(&cmd))
		goto LEnd;


LEnd:
	vpappb->EndLongOp();
	ReleasePpo(&pcfl);
	ReleasePpo(&pfil);
	return fTrue;
}


/****************************************************
 *
 * Browser Command Handler : (For deleting user snds)
 *
 ****************************************************/
bool BRWM::FCmdDel(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	long sid;
	TAG tag;
	PTAG ptag;
	PMSND pmsnd;
	STN stnErr;
	STN stnSnd;
	PMVU pmvu;

	if (_ithumSelect < _cthumCD)
		return fTrue;		// CD Sounds cannot be deleted

	pmvu = (PMVU)(_pstdio->Pmvie()->PddgGet(0));
 	AssertPo(pmvu, 0);
	_pgst->GetStn(_ithumSelect, &stnSnd);

	// Display the sound name in the help topic.
	AssertDo(vapp.FGetStnApp(idsDeleteSound, &stnErr), "String not present");
	if (vapp.TModal(vapp.PcrmAll(), ktpcQuerySoundDelete, &stnErr, bkYesNo,
			kstidQuerySoundDelete, &stnSnd) != tYes)
		{
		return fTrue;
		}

 	// Delete the midi or wave child chunk from the msnd
	// Invalidate the sound
	tag.sid = ksidUseCrf; 			// Non-CD-loaded content
	tag.pcrf = _pcrf;
	tag.ctg = kctgMsnd;
	_GetThumFromIthum(_ithumSelect, &tag.cno, &sid);

	pmsnd = (PMSND)vptagm->PbacoFetch(&tag, MSND::FReadMsnd);
	if (pvNil == pmsnd)
		return fTrue;

	if (!pmsnd->FInvalidate())
		Warn("Error invalidating sound");
	ReleasePpo(&pmsnd);

	if (!_FUpdateLists())
		{
		Release();	 	// Release browser; labels might be wrong
		return fTrue;
		}

	if (_ithumPageFirst >= _pglthd->IvMac())
		_ithumPageFirst--;
	if (_ithumPageFirst < 0)
		_ithumPageFirst = 0;

	_ithumSelect = ivNil;

	// Clear the selection if deleted
	tag.sid = ksidInvalid;
	ptag = pmvu->PtagTool();
	if (ptag->sid == ksidUseCrf && ptag->cno == tag.cno)
 		{
 		pmvu->SetTagTool(&tag);	 	// No need to close tag with ksidInvalid	
		}
	else if (ptag->sid != ksidInvalid)
		{
		// Show current cursor selection
		_ithumSelect = _IthumFromThum(ptag->cno, ptag->sid);
		}

	_SetScrollState();		// Set the state of the scroll arrows
	_pstdio->Pmvie()->Pmsq()->StopAll();
	FDraw();
	return fTrue;
}


/****************************************************
 *
 * Create a browser text object
 *
 * Returns:
 *	A pointer to the object, else pvNil.
 *
 ****************************************************/
PBRWT BRWT::PbrwtNew(PRCA prca, long kidPar, long kidGlass)
{
	AssertPo(prca, 0);

	PBRWT pbrwt;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidPar, kidGlass))
		return pvNil;

	if ((pbrwt = NewObj BRWT(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwt->_FInitGok(prca, kidGlass))	
		{
		ReleasePpo(&pbrwt);
		return pvNil;
		}

	pbrwt->_pgst = pvNil;

	return pbrwt;
}


/****************************************************
 *
 * Set the Gst for BRWT text
 *
 ****************************************************/	
void BRWT::SetGst(PGST pgst)
{
	AssertThis(0);
	AssertPo(pgst, 0);

	ReleasePpo(&_pgst);

	_pgst = pgst;
	_pgst->AddRef();
}

/****************************************************
 *
 * Initialize BRWT TGOB & text
 *
 ****************************************************/	
bool BRWT::FInit(PCMD pcmd, long thumSelect, long thumDisplay,
	PSTDIO pstdio, bool fWrapScroll, long cthumScroll)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	BRWD::Init(pcmd, thumSelect, thumDisplay, pstdio, fWrapScroll, cthumScroll);

	//
	// Create the tgob's for each frame on the page
	//
	if (!FCreateAllTgob())
		 return fFalse;

	vapp.DisableAccel();
	_fEnableAccel = fTrue;

	return fTrue;
}


/****************************************************
 *
 * Sets the ith text item in the child of the
 * current frame
 *
 ****************************************************/	
bool BRWT::_FSetThumFrame(long istn, PGOB pgobPar)
{
	AssertThis(0);
	AssertIn(istn, 0, _pgst->IvMac());
	AssertPo(pgobPar, 0);

	PTGOB ptgob;
	STN stnLabel;

	_pgst->GetStn(istn, &stnLabel);
	ptgob = (PTGOB)(pgobPar->PgobFirstChild());

	Assert(pvNil != ptgob, "No TGOB for the text");
	if (pvNil != ptgob)
		{
		Assert(ptgob->FIs(kclsTGOB), "GOB isn't a TGOB");
		if (ptgob->FIs(kclsTGOB))
			{
			ptgob->SetText(&stnLabel);
			return fTrue;
			}
		}

	return fFalse;
}


/****************************************************
 *
 * Create a BRoWser ActioN object
 *
 * Returns:
 *  A pointer to the view, else pvNil.
 *
 ****************************************************/
PBRWA BRWA::PbrwaNew(PRCA prca)
{
	AssertPo(prca, 0);

	PBRWA pbrwa;
	PGOK pgok;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidBackground, kidActionGlass))
		return pvNil;

	if ((pbrwa = NewObj BRWA(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwa->_FInitGok(prca, kidActionGlass))	
		{
		ReleasePpo(&pbrwa);
		return pvNil;
		}

	//
	// Stop the action browser animation while the browser is up.
	//
	pgok = (PGOK)vapp.Pkwa()->PgobFromHid(kidActorsActionBrowser);

	if ((pgok != pvNil) && pgok->FIs(kclsGOK))
		{
		Assert(pgok->FIs(kclsGOK), "Invalid class");
		pgok->FChangeState(kstSelected);
		}
							
	pbrwa->_ptgobPage = TGOB::PtgobCreate(kidBrowserPageNum, idsBrwsPageFont, tavCenter);
	return pbrwa;
}

/****************************************************
 *
 * Build the ape
 *
 ****************************************************/
bool BRWA::FBuildApe(PACTR pactr)
{
	AssertThis(0);
	AssertPo(pactr, 0);
	
	COST cost;
	PGOK pgokFrame;
	RC rcRel;
	GCB gcb;

	if (!cost.FGet(pactr->Pbody()))
		return fFalse;
	pgokFrame = (PGOK)vapp.Pkwa()->PgobFromHid(kidBrwsActionPrev);
	Assert(pgokFrame->FIs(kclsGOK), "Invalid class");
	rcRel.Set(krelZero, krelZero, krelOne, krelOne);
	gcb.Set(kidBrwsActionPrev, pgokFrame, fgobNil, kginDefault, pvNil, &rcRel);
	_pape = APE::PapeNew(&gcb, pactr->Ptmpl(), &cost, pactr->AnidCur(), fTrue);
	if (pvNil == _pape)
		return fFalse;
	return fTrue;
}


/***************************************************************************
 *
 * Build the string table for actions prior to action initialization
 * Transfer the string table to BRWA
 *
 * NOTE:  The string table is built from the template as the code already
 * has action names cached on the hard drive for selected actors. The
 * string table is built for fast scrolling.
 *
 **************************************************************************/
bool BRWA::FBuildGst(PSCEN pscen)
{
	AssertThis(0);

	STN stn;
	PTMPL ptmpl;
	long cactn;
	long iactn;
	PGST pgst;
	
	Assert(pvNil != pscen && pvNil != pscen->PactrSelected(),
		"kidBrwsAction: Invalid actor");

	ptmpl = pscen->PactrSelected()->Ptmpl();
	Assert(pvNil != ptmpl, "Actor has no template");

	if (pvNil == (pgst = GST::PgstNew(0)))
		return fFalse;

	cactn = ptmpl->Cactn();
	for (iactn = 0; iactn < cactn; iactn++)
		{
		if (!ptmpl->FGetActnName(iactn, &stn))
			goto LFail;
		if (!pgst->FAddStn(&stn))
			goto LFail;
		}

	SetGst(pgst);
	ReleasePpo(&pgst);

	if (!FBuildApe(pscen->PactrSelected()))
		goto LFail;

	if (!ptmpl->FIsTdt())
		_pape->SetCustomView(BR_ANGLE_DEG(0.0), BR_ANGLE_DEG(-30.0), BR_ANGLE_DEG(0.0));

	return fTrue;

LFail:
	ReleasePpo(&pgst);
	return fFalse;		
}


/****************************************************
 *
 * Process Browser Action Selection
 *
 ****************************************************/
void BRWA::_ProcessSelection(void)
{
	AssertThis(0);
	_pape->FSetAction(_ithumSelect);
	_pape->SetCycleCels(fTrue);
	_pape->FDisplayCel(_celnStart);	// Restart cycling at current display
	_celnStart = 0;		// When applying action, apply celn == 0
}

/****************************************************
 *
 * Browser Command Handler : Change cel (action brws)
 * pcmd->rglw[0] = kid of Change, Fwd, Back
 * pcmd->rglw[1] = bool reflection start/stop change cel
 *
 * Make button(s) invisible on single cel actions
 *
 ****************************************************/
bool BRWA::FCmdChangeCel(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);

	long st;
	long ccel;
	PACTR pactr = _pstdio->Pmvie()->Pscen()->PactrSelected();

	if (!pactr->Ptmpl()->FGetCcelActn(_pape->Anid(), &ccel))
		return fTrue;

	if (1 == ccel)
		st = kstBrowserInvisible;
	else
		st = kstBrowserSelected;

	if (_pape->FIsCycleCels())
		{
		_pape->SetCycleCels(fFalse);
		_celnStart = _pape->Celn();
		}

	if (1 == ccel)
		return fTrue;

	switch (pcmd->rglw[0])
		{
	case kidBrowserActionFwdCel:
		_celnStart++;
		_pape->FDisplayCel(_celnStart);
		break;
	case kidBrowserActionBackCel:
		_celnStart--;
		_pape->FDisplayCel(_celnStart);
		break;
	default:
		Bug("Unimplemented Change Cel kid");
		break;
		}
	return fTrue;
}


/****************************************************
 *
 * Create a BRoWser Import object
 * 		(A browser on top of a sound browser)
 * Returns:
 *  A pointer to the view, else pvNil.
 *
 ****************************************************/
PBRWI BRWI::PbrwiNew(PRCA prca, long kidGlass, long sty)
{
	AssertPo(prca, 0);

	PBRWI pbrwi;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidBackground, kidGlass))
		return pvNil;

	if ((pbrwi = NewObj BRWI(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwi->_FInitGok(prca, kidGlass))
		{
		ReleasePpo(&pbrwi);
		return pvNil;
		}

	pbrwi->_sty = sty;
	pbrwi->_ptgobPage = TGOB::PtgobCreate(kidImportPageNum, idsBrwsPageFont, tavCenter);
	return pbrwi;
}


/***************************************************************************
 *
 * Initialize the BRWI	 (Import Browser)
 *
 **************************************************************************/
bool BRWI::FInit(PCMD pcmd, CKI ckiRoot, PSTDIO pstdio)
{
	AssertBaseThis(0);

	PCFL pcfl;
	FNI fni;
	STN stn;

	_pstdio = pstdio;
	//
	// Create the gl's
	//
	if (pvNil == (_pglthd = GL::PglNew(size(THD), kglthdGrow)))
		return fFalse;			
	_pglthd->SetMinGrow(kglthdGrow);

	if (pvNil == (_pgst = GST::PgstNew(0)))
		return fFalse;

	_ckiRoot = ckiRoot;
	_bws = kbwsCnoRoot;

	// Fill the lists with sounds from the portfolio movie
	vapp.GetPortfolioDoc(&fni);
	pcfl = CFL::PcflOpen(&fni, fcflNil);
	if (pvNil == pcfl)
		goto LFail;	  // Error already reported
	_pcrf = CRF::PcrfNew(pcfl, 0);
	ReleasePpo(&pcfl);

	if (pvNil == _pcrf)
		goto LFail;
	if (!_FUpdateLists()) 	// to include sounds from the selected movie
		goto LFail;

	BRWD::Init(pcmd, ivNil, ivNil, pstdio, fTrue);
	if (!FCreateAllTgob())
		goto LFail;
	
	return fTrue;

LFail:
	return fFalse;
}


/****************************************************
 *
 * Create a BRoWser Prop/Actor object
 *
 * Returns:
 *  A pointer to the view, else pvNil.
 *
 ****************************************************/
PBRWP BRWP::PbrwpNew(PRCA prca, long kidGlass)
{
	AssertPo(prca, 0);

	PBRWP pbrwp;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidBackground, kidGlass))
		return pvNil;

	if ((pbrwp = NewObj BRWP(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwp->_FInitGok(prca, kidGlass))	
		{
		ReleasePpo(&pbrwp);
		return pvNil;
		}

	pbrwp->_ptgobPage = TGOB::PtgobCreate(kidBrowserPageNum, idsBrwsPageFont, tavCenter);
	return pbrwp;
}


/****************************************************
 *
 * Create a BRoWser Background object
 *
 * Returns:
 *  A pointer to the view, else pvNil.
 *
 ****************************************************/
PBRWB BRWB::PbrwbNew(PRCA prca)
{
	AssertPo(prca, 0);

	PBRWB pbrwb;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidBackground, kidSettingsGlass))
		return pvNil;

	if ((pbrwb = NewObj BRWB(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwb->_FInitGok(prca, kidSettingsGlass))	
		{
		ReleasePpo(&pbrwb);
		return pvNil;
		}

	pbrwb->_ptgobPage = TGOB::PtgobCreate(kidBrowserPageNum, idsBrwsPageFont, tavCenter);
	return pbrwb;
}

/****************************************************
 *
 * Background's virtual FCmdCancel
 *
 ****************************************************/
bool BRWB::FCmdCancel(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);
	AssertVarMem(_pstdio);

	PMVU pmvu;

 	// Update the tool
 	pmvu = (PMVU)(_pstdio->Pmvie()->PddgActive());
 	AssertPo(pmvu, 0);
 	pmvu->SetTool(toolDefault);

 	// Update the UI
 	_pstdio->Pmvie()->Pmcc()->ChangeTool(toolDefault);

	if ((vpappb->GrfcustCur() & fcustCmd) && (_pstdio->Pmvie()->Cscen() == 0))
		_pstdio->SetDisplayCast(fTrue);
	else
		_pstdio->SetDisplayCast(fFalse);
	return BRWB_PAR::FCmdCancel(pcmd);
}


/****************************************************
 *
 * Set the size of the pcrm 
 *
 ****************************************************/
void BRWL::_SetCbPcrmMin(void)
{
	AssertThis(0);
	long dwTotalPhys;
	long dwAvailPhys;

	// If short on memory, pull in the cache
	((APP *)vpappb)->MemStat(&dwTotalPhys, &dwAvailPhys);
	if (dwTotalPhys > kdwTotalPhysLim && dwAvailPhys > kdwAvailPhysLim)
		return;

	if (pvNil != _pcrm)
		{
		for (long icrf = 0; icrf < _pcrm->Ccrf(); icrf++)
			{
			_pcrm->PcrfGet(icrf)->SetCbMax(0);
			}
		}
}


/****************************************************
 *
 * Create a BRoWser Camera object
 *
 * Returns:
 *  A pointer to the view, else pvNil.
 *
 ****************************************************/
PBRWC BRWC::PbrwcNew(PRCA prca)
{
	AssertPo(prca, 0);

	PBRWC pbrwc;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidBackground, kidCameraGlass))
		return pvNil;

	if ((pbrwc = NewObj BRWC(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwc->_FInitGok(prca, kidCameraGlass))	
		{
		ReleasePpo(&pbrwc);
		return pvNil;
		}

	pbrwc->_ptgobPage = TGOB::PtgobCreate(kidBrowserPageNum, idsBrwsPageFont, tavCenter);
	return pbrwc;
}


/****************************************************
 *
 * Camera's virtual FCmdCancel
 *
 ****************************************************/
bool BRWC::FCmdCancel(PCMD pcmd)
{
	AssertThis(0);
	AssertVarMem(pcmd);
	AssertVarMem(_pstdio);

	PMVU pmvu;

 	// Update the tool
 	pmvu = (PMVU)(_pstdio->Pmvie()->PddgActive());
 	AssertPo(pmvu, 0);
 	pmvu->SetTool(toolDefault);

 	// Update the UI
 	_pstdio->Pmvie()->Pmcc()->ChangeTool(toolDefault);

	return BRWC_PAR::FCmdCancel(pcmd);
}

/****************************************************
 *
 * Create a BRoWser Roll Call object
 *
 ****************************************************/
PBRWR BRWR::PbrwrNew(PRCA prca, long kid)
{
	AssertPo(prca, 0);

	PBRWR pbrwr;
	GCB gcb;

	if (!_FBuildGcb(&gcb, kidBackground, kid))
		return pvNil;

	if ((pbrwr = NewObj BRWR(&gcb)) == pvNil)
		return pvNil;

	// Initialize the gok
	if (!pbrwr->_FInitGok(prca, kid))	
		{
		ReleasePpo(&pbrwr);
		return pvNil;
		}

	return pbrwr;
}

/****************************************************
 *
 * Initialize a BRoWser Roll Call object
 *
 ****************************************************/
bool BRWR::FInit(PCMD pcmd, CTG ctgTmplThum, long ithumDisplay, PSTDIO pstdio)
{
	AssertThis(0);
	
	long ccrf = 1;
	PCFL pcfl;
	BLCK blck;
	long ccki;
	long icki;
	CKI cki;
	TFC tfc;
	KID kid;
	FNET fnet;
	FNI fniThd;

	if (!fnet.FInit())
		return fFalse;

	_ctg = ctgTmplThum;
	BRWD::Init(pcmd, ivNil, ithumDisplay, pstdio, fFalse, 1);

	_pcrm = CRM::PcrmNew(ccrf);
	if (pvNil == _pcrm)
		goto LFail;		// Error already reported

	while(fnet.FNext(&fniThd))
		{
		if (fniThd.Ftg() != kftgThumbDesc)
			continue;
		pcfl = CFL::PcflOpen(&fniThd, fcflNil);
		if (pvNil == pcfl)
			{
			goto LFail;	  // Error already reported
			}
	
		// Add the file to the crm
		if (!_pcrm->FAddCfl(pcfl, kcbMaxCrm))
			goto LFail;	// Error already reported	

		// Create the cno map from tmpl->gokd
		if (ctgTmplThum == kctgTmth)
			ccki = pcfl->CckiCtg(kctgTmth);
		else
			ccki = pcfl->CckiCtg(kctgPrth);
		for (icki = 0; icki < ccki; icki++)
			{
			if (!pcfl->FGetCkiCtg(ctgTmplThum, icki, &cki))
				continue;

			// Read the chunk to map the cno of the CD content
			if (!pcfl->FFind(cki.ctg, cki.cno, &blck) ||
				!blck.FUnpackData())
				{
				goto LFail;
				}
			if (blck.Cb() != size(TFC))
				goto LFail;
			if (!blck.FReadRgb(&tfc, size(TFC), 0))
				goto LFail;
			if (kboCur != tfc.bo)
				SwapBytesBom(&tfc, kbomTfc);
			Assert(kboCur == tfc.bo, "bad TFC");

			if (!pcfl->FGetKidChidCtg(ctgTmplThum, cki.cno, 0, kctgGokd, &kid))
				{
				Warn("Actor content missing gokd");
				continue;
				}

			if (!_pstdio->FAddCmg(tfc.cno, kid.cki.cno))
				goto LFail;
			}
		}

	_fNoRepositionSel = fTrue;
	return fTrue;
LFail:
	Warn("Failed to initialize RollCall");
	return fFalse;
}

/****************************************************
 *
 * Update the RollCall : Select actor arid
 *
 ****************************************************/
bool BRWR::FUpdate(long arid, PSTDIO pstdio)
{
	AssertThis(0);
	long ithumDisplay;

	_pstdio = pstdio;
	_ithumSelect = ithumDisplay = _IthumFromArid(arid);

	// Define the index of the first thum on the page
	if (ithumDisplay != ivNil)
		{
		if (!_fNoRepositionSel || 
			ithumDisplay < _ithumPageFirst || 
			ithumDisplay >= _ithumPageFirst + _cfrm)
			{
			_ithumPageFirst = ithumDisplay;
			}
		}

	// Define the number of frames on the previous page and
	// account for wrap-around
	_SetScrollState();		// Set the state of the scroll arrows

	return FDraw();
}


/****************************************************
 *
 * Process Browser Roll Call Selection
 * As the RollCall Browsers do not have OK buttons,
 * the selection is applied now.
 *
 * NOTE: The RollCall scripts cannot generate FCmdOk
 * as that would abort the browser script out from
 * underneath the code.
 *
 ****************************************************/
void BRWR::_ProcessSelection(void)
{
	AssertThis(0);
	
	long thumSelect;
	long sid;

	// Get Selection from virtual function
	_GetThumFromIthum(_ithumSelect, &thumSelect, &sid);
	
	// Apply the selection
	_ApplySelection(thumSelect, sid);
}


/****************************************************
 *
 * Browser Roll Call Cthum
 *
 ****************************************************/
long BRWR::_Cthum(void)
{
	AssertThis(0);
	STN stn;
	long iarid;
	long arid;
	long cactRef;
	long cthum = 0;
	bool fProp;

	Assert(_ctg == kctgPrth || _ctg == kctgTmth,
		"Invalid BRWR initialization");

	if (_pstdio->Pmvie() == pvNil)
		return 0;

	for (iarid = 0;
		_pstdio->Pmvie()->FGetArid(iarid, &arid, &stn, &cactRef);
		iarid++)
		{
		// Verify actor in correct browser
		fProp = _pstdio->Pmvie()->FIsPropBrwsIarid(iarid);

		if (_ctg == kctgPrth && !fProp)
			continue;
		if (_ctg == kctgTmth && fProp)
			continue;
		if (!cactRef)
			continue;
		cthum++;
		}
	return cthum;
}


/****************************************************
 *
 * Iarid from Ithum
 *
 ****************************************************/
long BRWR::_IaridFromIthum(long ithum, long iaridFirst)
{
	AssertThis(0);
	STN stn;
	long iarid;
	long arid;
	long cactRef;
	bool fProp;
	long cthum = 0;

	if (_pstdio->Pmvie() == pvNil)
		return ivNil;

	for (iarid = iaridFirst;
		_pstdio->Pmvie()->FGetArid(iarid, &arid, &stn, &cactRef);
		iarid++)
		{
		// Verify actor in correct browser
		fProp = _pstdio->Pmvie()->FIsPropBrwsIarid(iarid);

		if (_ctg == kctgPrth && !fProp)
			continue;
		if (_ctg == kctgTmth && fProp)
			continue;
		if (!cactRef)
			continue;
		if (cthum == ithum)
			return iarid;
		cthum++;
		}
	return iarid;
}

/****************************************************
 *
 * Ithum from AridSelect
 * (eg, find the thumbnail corresponding to the correct
 * copy of Billy)
 *
 ****************************************************/
long BRWR::_IthumFromArid(long aridSelect)
{
	AssertThis(0);

	STN stn;
	long iarid;								
	long arid;
	long cactRef;
	bool fProp;
	long ithum = 0;

	if (_pstdio->Pmvie() == pvNil)
		return ivNil;

	for (iarid = 0;
		_pstdio->Pmvie()->FGetArid(iarid, &arid, &stn, &cactRef);
		iarid++)
		{
		AssertDo(_pstdio->Pmvie()->FGetArid(iarid, &arid, &stn, &cactRef),
			"Arid should exist");
		// Verify actor in correct browser
		fProp = _pstdio->Pmvie()->FIsPropBrwsIarid(iarid);
		if (arid == aridSelect)
			{
			if ((_ctg == kctgPrth) && !fProp)
				return ivNil;
			if ((_ctg == kctgTmth) && fProp)
				return ivNil;
			return ithum;
			}

		if (_ctg == kctgPrth && !fProp)
			continue;
		if (_ctg == kctgTmth && fProp)
			continue;
		if (!cactRef)
			continue;
		ithum++;
		}
	return ivNil;
}


/****************************************************
 *
 * Sets the ith Gob as a child of the current frame
 * Advance the gob (thumbnail) index
 *
 ****************************************************/	
bool BRWR::_FSetThumFrame(long ithum, PGOB pgobPar)
{
	AssertThis(0);
	AssertIn(ithum, 0, _pstdio->Pmvie()->CmactrMac());
	AssertPo(pgobPar, 0);

	RC rcAbsPar;
	RC rcAbs;
	RC rcRel;
	STN stnLabel;
	long stid;
	long cactRef;
	long arid;
	long iarid;
	TAG tag;
	STN stn;
	long dxp;
	long dyp;

	// Associate the gob with the current frame
	iarid = _IaridFromIthum(ithum);
	if (!_pstdio->Pmvie()->FGetArid(iarid, &arid, &stn, &cactRef, &tag))
		return fFalse;
	
	_pstdio->Pmvie()->FGetName(arid, &stnLabel);

	if (!_pstdio->Pmvie()->FIsIaridTdt(iarid))
		{
		PGOK pgok;
		CNO cno = _pstdio->CnoGokdFromCnoTmpl(tag.cno);
		long kidThum = _KidThumFromIfrm(_cfrmPageCur);
		pgok = vapp.Pkwa()->PgokNew(pgobPar, kidThum, cno, _pcrm);
		if (pvNil == pgok)
			return fFalse;

		// Note: The graphic is not the correct size
		((PGOB)pgok)->GetPos(&rcAbs, &rcRel);
		pgobPar->GetPos(&rcAbsPar, pvNil);
		dxp = (rcAbs.Dxp() - rcAbsPar.Dxp()) / 2;
		dyp = (rcAbs.Dyp() - rcAbsPar.Dyp()) / 2;
		rcAbs.xpLeft += (_dxpFrmOffset - dxp);
		rcAbs.ypTop += (_dypFrmOffset - dyp);
		rcAbs.xpRight += (dxp - _dxpFrmOffset);
		rcAbs.ypBottom += (dyp - _dypFrmOffset);
		((PGOB)pgok)->SetPos(&rcAbs, &rcRel);
		}
	else
		{
		PTGOB ptgob;
		STN stn = stnLabel;
		long cch = stn.Cch();
		long hidThum;

		// Display the text as the thumbnail
		hidThum = _KidThumFromIfrm(_cfrmPageCur);
		ptgob = TGOB::PtgobCreate(_kidFrmFirst + _cfrmPageCur, _idsFont, tavCenter, hidThum);

		if (pvNil != ptgob)
			{
			ptgob->SetText(&stn);
			}
		}
		
	// Put the name in the global string registry for rollover help
	stid = (_ctg == kctgPrth) ? kstidProp : kstidActor;
	stid += _cfrmPageCur;
	return vapp.Pkwa()->Pstrg()->FPut(stid, &stnLabel);
}

 /****************************************************
 *
 * Clear rollover help
 *
 ****************************************************/	
bool BRWR::_FClearHelp(long ifrm)
{
	AssertThis(0);
	AssertIn(ifrm, 0, _cfrm);
	
	STN stn;
	stn.SetNil();
	long stid = (_ctg == kctgPrth) ? kstidProp : kstidActor;
	stid += ifrm;
	return vapp.Pkwa()->Pstrg()->FPut(stid, &stn);
}

 /****************************************************
 *
 * Release previous thum from frame ifrm
 * Assumes gob based.
 *
 ****************************************************/	
void BRWR::_ReleaseThumFrame(long ifrm)
{
	AssertThis(0);
	AssertIn(ifrm, 0, _cfrm);
	PGOB pgob;

	// Release previous gob associated with the current frame
	pgob = _PgobFromIfrm(ifrm);
	if (pvNil != pgob)
		{
		ReleasePpo(&pgob);
		}
}



/****************************************************
 *
 * Browser Destructor
 *
 ****************************************************/
BRWD::~BRWD(void)
{
	AssertBaseThis(0);
	ReleasePpo(&_pbrcn);
	vpappb->FSetProp(kpridBrwsOverrideThum, -1);
	vpappb->FSetProp(kpridBrwsOverrideKidThum, -1);
}

/****************************************************
 *
 * Browser List Destructor
 *
 ****************************************************/
BRWL::~BRWL(void)
{
	AssertBaseThis(0);
	ReleasePpo(&_pglthd);
	ReleasePpo(&_pcrm);
	ReleasePpo(&_pgst);

	if (_fEnableAccel)
		vapp.EnableAccel();
}

/****************************************************
 *
 * Browser Text Destructor
 *
 ****************************************************/
BRWT::~BRWT(void)
{
	AssertBaseThis(0);
	ReleasePpo(&_pgst);

	if (_fEnableAccel)
		vapp.EnableAccel();
}

/****************************************************
 *
 * Browser RollCall Destructor
 *
 ****************************************************/
BRWR::~BRWR(void)
{
	AssertBaseThis(0);
	ReleasePpo(&_pcrm);
}

/****************************************************
 *
 * Browser Music Import Destructor
 *
 ****************************************************/
BRWI::~BRWI(void)
{
	AssertBaseThis(0);
	ReleasePpo(&_pcrf);
}

/****************************************************
 *
 * Browser Context Destructor
 *
 ****************************************************/
BRCNL::~BRCNL(void)
{
	AssertBaseThis(0);
	ReleasePpo(&pglthd);
	ReleasePpo(&pgst);
	ReleasePpo(&pcrm);
}

#ifdef DEBUG
/****************************************************

	Mark memory used by the BRCN

 ****************************************************/
void BRCN::MarkMem(void)
{
	AssertThis(0);
	BRCN_PAR::MarkMem();
}

/****************************************************

	Mark memory used by the BRCNL

 ****************************************************/
void BRCNL::MarkMem(void)
{
	AssertThis(0);
	MarkMemObj(pglthd);
	MarkMemObj(pgst);
	MarkMemObj(pcrm);
	BRCNL_PAR::MarkMem();
}

/****************************************************

	Mark memory used by the BRWR

 ****************************************************/
void BRWR::MarkMem(void)
{
	AssertThis(0);
	MarkMemObj(_pcrm);
	BRWR_PAR::MarkMem();
}


/****************************************************

	Mark memory used by the BRWI

 ****************************************************/
void BRWI::MarkMem(void)
{
	AssertThis(0);
	MarkMemObj(_pcrf);
	BRWI_PAR::MarkMem();
}


/****************************************************

	Mark memory used by the BRWD

 ****************************************************/
void BRWD::MarkMem(void)
{
	AssertThis(0);
	BRWD_PAR::MarkMem();
	MarkMemObj(_pbrcn);
}


/****************************************************

	Mark memory used by the BRWL

 ****************************************************/
void BRWL::MarkMem(void)
{
	AssertThis(0);

	BRWL_PAR::MarkMem();
	MarkMemObj(_pglthd);
	MarkMemObj(_pcrm);
	MarkMemObj(_pgst);
}


/****************************************************

	Mark memory used by the BRWT

 ****************************************************/
void BRWT::MarkMem(void)
{
	AssertThis(0);

	MarkMemObj(_pgst);
	BRWT_PAR::MarkMem();
}

/****************************************************

	Mark memory used by the BRWA

 ****************************************************/
void BRWA::MarkMem(void)
{
	AssertThis(0);

	MarkMemObj(_pape);
	BRWA_PAR::MarkMem();
}

/****************************************************

	BCL Markmem

 ****************************************************/
void BCL::MarkMem(void)
{
	BCL_PAR::MarkMem();
	MarkMemObj(_pglthd);
}

void BCLS::MarkMem(void)
{
	BCLS_PAR::MarkMem();
	MarkMemObj(_pgst);
}
  

/****************************************************

	Assert the validity of the BRCN

 ****************************************************/
void BRCN::AssertValid(ulong grfobj)
{
	BRCN_PAR::AssertValid(fobjAllocated);
}

/****************************************************

	Assert the validity of the BRCNL

 ****************************************************/
void BRCNL::AssertValid(ulong grfobj)
{
	BRCNL_PAR::AssertValid(fobjAllocated);
}

/****************************************************

	BCL AssertValid

 ****************************************************/
void BCLS::AssertValid(ulong grf)
{
	BCLS_PAR::AssertValid(grf);
	AssertPo(_pgst, 0);
}
void BCL::AssertValid(ulong grf)
{
	BCL_PAR::AssertValid(grf);
	AssertPo(_pglthd, 0);
}


/****************************************************

	Assert the validity of the BRWD

 ****************************************************/
void BRWD::AssertValid(ulong grfobj)
{
	BRWD_PAR::AssertValid(fobjAllocated);
	AssertNilOrPo(_pbrcn, 0);
}

/****************************************************

	Assert the validity of the BRWL

 ****************************************************/
void BRWL::AssertValid(ulong grfobj)
{
	
	BRWL_PAR::AssertValid(fobjAllocated);
	AssertNilOrPo(_pgst, 0);
	AssertNilOrPo(_pglthd, 0);
	AssertNilOrPo(_pcrm, 0);
}

/****************************************************

	Assert the validity of the BRWR

 ****************************************************/
void BRWR::AssertValid(ulong grfobj)
{
	
	BRWR_PAR::AssertValid(fobjAllocated);
	AssertNilOrPo(_pcrm, 0);
}

/****************************************************

	Assert the validity of the BRWI

 ****************************************************/
void BRWI::AssertValid(ulong grfobj)
{
	
	BRWI_PAR::AssertValid(fobjAllocated);
	AssertNilOrPo(_pcrf, 0);
}

/****************************************************

	Assert the validity of the BRWT

 ****************************************************/
void BRWT::AssertValid(ulong grfobj)
{
	BRWT_PAR::AssertValid(fobjAllocated);
	AssertNilOrPo(_pgst, 0);
}	

/****************************************************

	Assert the validity of the BRWA

 ****************************************************/
void BRWA::AssertValid(ulong grfobj)
{
	BRWA_PAR::AssertValid(fobjAllocated);
	AssertNilOrPo(_pape, 0);
}	
#endif //DEBUG
